package com.wlz.oauth2.config;

import com.wlz.oauth2.enhancer.JwtTokenEnhancer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.ArrayList;
import java.util.List;

/**
 *  配置授权服务器
 *     基于内存配置
 *
 *    整合JWT
 *
 *       配置：
 *          1） 引入依赖
 *          2） 添加jwt配置类
 *                  com.wlz.oauth2.config.JwtTokenStoreConfig
 *          3）修改授权服务器配置类
 *              com.wlz.oauth2.config.AuthorizationServerJwtConfig#configure(org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer)
 *                   .tokenStore(jwtTokenStore) // 配置存储令牌策略
 *                   .accessTokenConverter(jwtAccessTokenConverter)
 *
 *      测试（通过密码模式测试 注意：只有密码模式和授权码模式才有刷新令牌）
 *          1. 获取令牌  通过浏览器测试，需要配置支持get请求和表单验证 (浏览器地址栏输入，或者使用工具，例如 Talend API Tester )
 *                   http://localhost:8080/oauth/token?username=wlz&password=123456&grant_type=password&client_id=client&client_secret=123456&scope=all
 *              直接返回令牌 jwt json 格式
 *
 *          2. 访问资源 （方式有多种）
 *              方式1：
 *                   http://localhost:8080/user/getCurrentUser?access_token=37b086c0-3c4e-4bfa-a200-32a08d290156
 *              方式2: (headers 里 加 Authorization ： bearer 37b086c0-3c4e-4bfa-a200-32a08d290156)
 *                  http://localhost:8080/user/getCurrentUser
 *       刷新令牌：
 *          http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=[refresh_token值]
 *          http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=de9c7723-78cb-4a01-95b5-0cc4c497e2b8
 *
 *
 *
 * @author wlz
 * @desc
 * @date 2021-10-05 14:39
 */
//@Configuration
//@EnableAuthorizationServer
public class AuthorizationServerJwtConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManagerBean;

    @Autowired
    private UserDetailsService myUserDetailsService;

    @Autowired
    private TokenStore jwtTokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置JWT的内容增强器
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(delegates);
        endpoints.authenticationManager(authenticationManagerBean) // 使用密码模式需要配置
                .tokenStore(jwtTokenStore) // 配置存储令牌策略
                .accessTokenConverter(jwtAccessTokenConverter) // 配置
                .reuseRefreshTokens(false) // refresh_token是否重复使用
                .userDetailsService(myUserDetailsService) // 刷新令牌授权包含对用户信息的检查
        .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); // 支持 GET POST 请求
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients(); // 允许表单登陆认证
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        //刷新令牌
        // http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=de9c7723-78cb-4a01-95b5-0cc4c497e2b8

        clients.inMemory()
                .withClient("client") // 配置 client_id
                .secret(passwordEncoder.encode("123456")) // 配置 client_secret
                .accessTokenValiditySeconds(3600) // 配置访问token的有效期
                .refreshTokenValiditySeconds(864000) // 配置刷新token的有效期
                .redirectUris("https://www.baidu.com") // 配置redirect_uri，用于授权成功后跳转
                .scopes("all")  // 配置申请的权限范围
                // 配置grant_type，表示授权类型 authorization_code: 授权码模式; implicit: 简化模式; password: 密码模式; client_credentials：客户端模式；refresh_token:刷新令牌;
                .authorizedGrantTypes("authorization_code", "implicit","password","client_credentials","refresh_token");
    }
}
